/*
 * Copyright 2007 Brian Watkins
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package bw.news;

import java.io.*;
import java.util.*;

public class NewsFeed extends LinkedList implements Serializable
{
    static final long serialVersionUID = -4182864383798299784L;

    private Vector _errorNews = new Vector();
    private boolean _newItemsSeen = false;

    public NewsFeed ()
    {
        super();
    }

    public void setErrorNews ( NewsItem item )
    {
        _errorNews.addElement( item );
    }

    public boolean hasError ()
    {
        return ( _errorNews.size() > 0 );
    }

    public void updateFeed ( NewsFeed feed )
    {
        //first reduce this to the intersection with the new feed.
        //without removing any starred items
        Iterator me = super.iterator();
        while ( me.hasNext() )
        {
            NewsItem item = (NewsItem) me.next();
            if ( !feed.contains( item ) && !item.isStarred() )
            {
                me.remove();
            }
        }
        //this.retainAll( feed );
        
        //then add the new items to me.
        //first reverse the order of the feed so when we add an item successively
        //to the front of the list it will end up in the right order
        //Then only add an element if we don't already have it
        //note we are assuming here that there's no pub date on these items.
        //if there is, the feed will be resorted according to that
        Collections.reverse( feed );
        Iterator items = feed.iterator();
        while ( items.hasNext() )
        {
            NewsItem item = (NewsItem) items.next();
            if ( !contains( item ) ) 
            {
                addFirst( item );

                //set this flag to mark that there are new unseen items
                _newItemsSeen = false;
            }
        }

        //if the items in this feed support sort then do it
        if ( supportsSort() )
        {
            Collections.sort( this, new NewsItemComparator() );
        }

        //get rid of any error news since we've successfully updated the feed
        _errorNews.clear();
    }

    public Iterator iterator ()
    {
        //if the errorNews is set then just return that. this 
        //means there was an unrecovreable error in th feed. otherwise 
        //just return the iterator of the feed
        return ( _errorNews.size() > 0 ? _errorNews.iterator() : super.iterator() );
    }

    public void setNewItemsSeen ()
    {
        _newItemsSeen = true;
    }

    public boolean newItemsSeen ()
    {
        return _newItemsSeen;
    }

    public int unseenItemsSize ()
    {
        int newItems = 0;

        //return the number of new items only if
        //we haven't viewed them yet on the ui
        if ( !_newItemsSeen )
        {
            Iterator news = iterator();
            
            while ( news.hasNext() )
            {
                NewsItem item = (NewsItem) news.next();
                if ( item.isNew() )
                {
                    ++newItems;
                }
            }
        }

        return newItems;
    }

    public void markNewsItemsOldIfSeen ()
    {
        //if the new items have been seen then update them
        //to old. this is called at the beginning of an update
        //cycle. 
        if ( _newItemsSeen )
        {
            Iterator news = iterator();
        
            while ( news.hasNext() )
            {
                NewsItem item = (NewsItem) news.next();
                item.setNew( false );
            }
        }
    }

    public boolean setItemStarred ( String id, boolean b )
    {
        boolean foundItem = false;

        Iterator me = super.iterator();
        while ( me.hasNext() )
        {
            NewsItem item = (NewsItem) me.next();
            if ( item.getId().equals( id ) )
            {
                item.setStarred( b );
                foundItem = true;
                break;
            }
        }

        return foundItem;
    }

    private boolean supportsSort ()
    {
        //we can only sort these items if they have published dates
        //but not all feeds support this. If no pub date then we will
        //let the order be determined by the position in the news feed.
        boolean shouldSort = ( size() > 0 );

        Iterator items = iterator();
        while ( items.hasNext() )
        {
            NewsItem item = (NewsItem) items.next();
            if ( !item.hasPublishedDate() )
            {
                shouldSort = false;
                break;
            }
        }
        
        return shouldSort;
    }

    class NewsItemComparator implements Comparator
    {
        public NewsItemComparator()
        {
            //nothing
        }

        public int compare ( Object o1, Object o2 )
        {
            NewsItem a = (NewsItem) o1;
            NewsItem b = (NewsItem) o2;

            //sort by published date, by switching the compare order of
            //the two objects we automatically sort in reverse order which
            //is what we want -- items with most recent pub date are first
            return b.getPublishedDate().compareTo( a.getPublishedDate() );
        }

        public boolean equals ( Object obj )
        {
            return ( this.hashCode() == obj.hashCode() );
        }
        
    }

}